feat(cdp-interact): setFieldValue action for React Hook Form fallback (closes #126 Gap A)#179
Merged
Merged
Conversation
…closes #126 Gap A) Adds cdp_interact action="setFieldValue" — explicit React Hook Form escape hatch for when typeText's onChangeText/onChange descendant walk can't find a typeable handler. Closes Gap A of #126. Background: typeText handles the wrapper-Pressable case (depth-bounded descendant walk for inner TextInput) but fails when a field's value flows through field.onChange → FormProvider context → setValue with no inner TextInput-shaped fiber to find. Common with design-system TextFields built as custom Controllers around <Pressable> + custom rendering, where the agent reports "no handler" but the form IS fillable via context.setValue. API: cdp_interact({ action: 'setFieldValue', testID: 'email-field', // anchor inside the form subtree name: 'email', // RHF field name (matches useController/Controller name) value: 'a@b.com', // any string | number | boolean, passed verbatim shouldValidate: true, // default true; pass-through to setValue options shouldDirty: true, // default true; pass-through to setValue options }) Algorithm (injected-helpers.ts setFieldValue handler): - Match the testID anchor (existing infrastructure) - Walk UP via node.return, bounded at 32 levels / 100 fiber visits - Look for the first Provider fiber whose memoizedProps.value duck-types as UseFormReturn: typeof value.setValue === 'function' AND typeof value.getValues === 'function' AND value.control is an object - Call value.setValue(name, value, {shouldValidate, shouldDirty}) - Surface the thrown message if setValue rejects (bad field name etc.) Closest ancestor wins (natural React context resolution) — nested forms behave intuitively. Why walk-up via fiber.return instead of useFormContext(): injected helpers run OUTSIDE React's rendering cycle, so hooks can't be called. The codebase already uses this pattern (memoizedProps walk) for nav and store context — adding setFieldValue follows the established convention. Why explicit action instead of implicit typeText fallback: the design question was deliberate (per discussion). Implicit fallback would change the meaning of an existing tool call and risk surprising users who expected the existing "no handler" error. Explicit setFieldValue is opt-in, self-contained, and documented in the tool description. Tests (8 new in gh-126-set-field-value.test.js, all green): - happy path: walks up to FormProvider, calls setValue with defaults - shouldValidate/shouldDirty pass through verbatim - numeric and boolean values pass through without coercion - nested-forms: closest provider wins (React context semantics) - missing opts.name → clear error, does NOT walk the tree - no FormProvider ancestor → actionable hint pointing at <FormProvider> - setValue throwing → caught + thrown message surfaced - duck-type rejects Providers missing setValue (e.g. ThemeProvider) Other changes: - Helpers bundle version bumped 21 → 22 (existing source-guard test + version-pin assertion both updated) - cdp_interact schema: action enum gains 'setFieldValue' + 'longPress' (latter was already in the description string but missing from the TS type union — fixed for consistency) - New args: name, value, shouldValidate, shouldDirty (all optional; required only for setFieldValue, enforced at handler boundary) Note: codex-pair did not review this commit (the codex MCP server appears disconnected this session — no log entries since May 20). The 8-test coverage + the explicit design approval are the safety net. Verified: 1514/1514 cdp-bridge unit tests passing (+8 net new). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes Gap A of #126 — adds a new
cdp_interact action=\"setFieldValue\"for the case wheretypeText's descendant walk can't find a typeable handler because the field's state flows through React Hook Form'sfield.onChange→ FormProvider context →setValue, with no inner TextInput-shaped fiber fortypeTextto find.Gap B (action-sheet portal modal invisibility) is a different fiber-root iteration problem — deferred to a follow-up.
API
Algorithm (injected-helpers.ts)
node.return, bounded at 32 levels / 100 fiber visits.memoizedProps.valueduck-types asUseFormReturn:typeof value.setValue === 'function' && typeof value.getValues === 'function' && value.control && typeof value.control === 'object'.value.setValue(name, value, { shouldValidate, shouldDirty }).setValuerejects (bad field name, value type mismatch, transitioning state).Closest ancestor wins → nested forms behave like natural React context resolution.
Design decisions
Why explicit action, not implicit
typeTextfallback. Implicit fallback would change the meaning of an existing tool call and risk surprising users who expected the existing 'no handler' error. ExplicitsetFieldValueis opt-in, self-contained, and documented in the tool description.Why walk-up via
fiber.returninstead ofuseFormContext(). Injected helpers run OUTSIDE React's rendering cycle, so hooks can't be called. The codebase already uses this fiber-walk pattern for nav state and store context — adding setFieldValue follows the established convention.Why a strict duck-type (setValue + getValues + control all required). Many Providers expose
valueobjects with one or two look-alike fields. ThemeProvider with.value.theme.setValue(a custom setter) shouldn't be mistaken for a form. Requiring all three RHF surface markers gives a clean signal.Test plan
8 new tests in
gh-126-set-field-value.test.js, all using a VM sandbox with synthetic fiber trees (consistent with existing injected-helpers tests):shouldValidate=falseandshouldDirty=falsereach setValue verbatimopts.namereturns clear error without walking the tree<FormProvider>valuelackssetValueTotal: 1514/1514 cdp-bridge unit tests passing (+8 net new).
Other changes:
cdp_interactaction enum gains'setFieldValue'AND'longPress'(latter was in the description string but missing from the TypeScript type union — fixed for consistency)name,value,shouldValidate,shouldDirty(all optional; required only for setFieldValue, enforced at handler boundary)Note on review
codex-pair did not review this commit — the
ask-codexMCP server appears disconnected this session (no log entries since May 20). The 8-test coverage + the explicit design approval before implementation are the safety net. Manual/codex-reviewwelcome from reviewers if a deeper second-opinion is wanted.Refs
scripts/cdp-bridge/src/injected-helpers.ts:1250— newsetFieldValuehandlerscripts/cdp-bridge/src/tools/interact.ts— handler args extension + validationscripts/cdp-bridge/src/index.ts:509— Zod schema + tool description🤖 Generated with Claude Code